package jcombobox;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Stack;

import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;
/*
 * Created on Mar 26, 2007
 */

/**
 * @author Anand
 */
public class JComboBoxDemo extends JFrame{

	Container container;
	JComboBox jcombo;
	JTextComponent editor;
	String []comboItems = {"Again","Assign","Attribute",
							"Bell","Bowl","Bucket",
							"Certificate","Comment","Custom"};
	
	public JComboBoxDemo()  {
		super("JComboBox Demo");
		
		container = getContentPane();
		
		jcombo = new JComboBox(comboItems);
		
		jcombo.setEditable(true);
		
		jcombo.addItemListener(new ItemListener(){
			public void itemStateChanged(ItemEvent event) {
				
				if( event.getSource() == jcombo
						&& event.getStateChange() == ItemEvent.SELECTED )
				{
					System.out.println( "Change:"
									+ jcombo.getSelectedItem() + " at index:" + jcombo.getSelectedIndex());
				}
			}
		});
		
		editor=(JTextComponent) jcombo.getEditor().getEditorComponent();		
		
		editor.setDocument(new AutoComboFiller(jcombo));
		
		setLayout(new FlowLayout());
		
		container.add(jcombo);
		
		setSize(200,80);
		
		setResizable(false);
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
	}
	
	public static void main(String[] args) {
		new JComboBoxDemo().setVisible(true);
	}
//custom class to provide auto-fill fuctionality to combobox.
	class AutoComboFiller extends PlainDocument{
		//The PlainDocument Api is used/inherited to provide auto-fill functionality.
		JComboBox combo;
		JTextComponent editor;
		ComboBoxModel model;
		boolean selecting = false;
		Stack stk;
		
		AutoComboFiller(final JComboBox combo){
			this.combo=combo;
			model=combo.getModel();
			editor=(JTextComponent) jcombo.getEditor().getEditorComponent();
			
			combo.addActionListener(new ActionListener() {
	            public void actionPerformed(ActionEvent e) {
	                if (!selecting) highlightCompletedText(0);
	            }
	        });
	        editor.addKeyListener(new KeyAdapter() {
	            public void keyPressed(KeyEvent e) {
	            	char character=e.getKeyChar();
	            	int code=e.getKeyCode();
	            	// provide/call here a sorting function that sorts the items inside the combobox.
	            	// sample sorting given below, use sorting techniques for full featured support.
	            	
	            	switch(character){
	            		case 'A':
	            		case 'a':
	            			combo.removeAllItems();
	            			for(int length=0;length<3;length++){
	            				combo.addItem(comboItems[length]);
	            			}
	            			break;
	            		case 'B':
	            		case 'b':
	            			combo.removeAllItems();
	            			for(int length=3;length<6;length++){
	            				combo.addItem(comboItems[length]);
	            			}
	            			break;
	            		case 'C':
	            		case 'c':
	            			combo.removeAllItems();
	            			for(int length=6;length<9;length++){
	            				combo.addItem(comboItems[length]);
	            			}
	            			break;
	            	}
	            	
	                if (combo.isDisplayable()) combo.setPopupVisible(true);
	            }
	        });
		}
		
		public void remove(int offs, int len) throws BadLocationException {
	        // returns immediately when selecting an item
	        if (selecting) return;
	        super.remove(offs, len);
	    }
		
		public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
	        // return immediately when selecting an item
	        if (selecting) return;
	        // insert the string into the document
	        super.insertString(offs, str, a);
	        // lookup and select a matching item
	        Object item = lookupItem(getText(0, getLength()));
	        if (item != null) {
	            setSelectedItem(item);
	        } else {
	            // keep old item selected if there is no match
	            item = combo.getSelectedItem();
	            // imitate no insert (later on offs will be incremented by string.length() 
	            //selection won't move forward)
	            offs = offs-str.length();
	            // provide feedback to the user that his input has been received but can not be accepted
	            //combo.getToolkit().beep(); 
	            
	        }
	        setText(item.toString());
	        // select the completed part
	        highlightCompletedText(offs+str.length());
	    }
	    
	    private void setText(String text) throws BadLocationException {
	        // remove all text and insert the completed string
	        super.remove(0, getLength());
	        super.insertString(0, text, null);
	    }
	    
	    private void highlightCompletedText(int start) {
	        editor.setSelectionStart(start);
	        editor.setSelectionEnd(getLength());
	    }
	    
	    private void setSelectedItem(Object item) {
	        selecting = true;
	        model.setSelectedItem(item);
	        selecting = false;
	    }
	    
	    private Object lookupItem(String pattern) {
	        Object selectedItem = model.getSelectedItem();
	        // only search for a different item if the currently selected does not match
	        if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) {
	            return selectedItem;
	        } else {
	            // iterate over all items
	            for (int i=0, n=model.getSize(); i < n; i++) {
	                Object currentItem = model.getElementAt(i);
	                // current item starts with the pattern ?
	                if (startsWithIgnoreCase(currentItem.toString(), pattern)) {
	                    return currentItem;
	                }
	            }
	        }
	        // no item starts with the pattern will either return pattern or return null
	        //System.out.println("No item matches pattern '" + pattern + "'");
	        return pattern;
	        //return null;
	    }
	    
	    // checks if str1 starts with str2 - ignores case
	    private boolean startsWithIgnoreCase(String str1, String str2) {
	        return str1.toUpperCase().startsWith(str2.toUpperCase());
	    }
	}
}	
